/**
 * yatejingle.h
 * Yet Another Jingle Stack
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2004-2013 Null Team
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef __YATEJINGLE_H
#define __YATEJINGLE_H

#include <yateclass.h>
#include <yatejabber.h>

/**
 * Holds all Telephony Engine related classes.
 */
namespace TelEngine {

class JGRtpMedia;                        // A Jingle RTP data payload
class JGCrypto;                          // Content crypto data
class JGRtpMediaList;                    // A list of Jingle RTP data payloads
class JGRtpCandidate;                    // A RTP transport candidate
class JGRtpCandidates;                   // A list of RTP transport candidates
class JGSessionContent;                  // A Jingle session content
class JGStreamHost;                      // A Jingle file transfer stream host
class JGSession;                         // A basic Jingle session
class JGSession0;                        // A session implementing the old jingle protocol
class JGSession1;                        // The version 1 of a jingle session
class JGEvent;                           // An event generated by a Jingle session
class JGEngine;                          // The Jingle engine
class JGSentStanza;                      // Sent stanza timeout info


/**
 * This class holds a Jingle data payload description
 * @short A Jingle data payload
 */
class YJABBER_API JGRtpMedia : public GenObject
{
public:
    /**
     * Constructor. Fill this object from the given attributes
     * @param id The 'id' attribute
     * @param name The 'name' attribute
     * @param clockrate The 'clockrate' attribute
     * @param synonym Application synonym for this payload
     * @param channels Optional 'channels' attribute (the number of channels)
     * @param pTime Optional "ptime" attribute (packet time)
     * @param maxPTime Optional "maxptime" attribute (maximum packet time)
     * @param bitRate Optional "bitrate" attribute
     */
    inline JGRtpMedia(const char* id, const char* name, const char* clockrate,
	const char* synonym, const char* channels = 0,
	const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0)
	: m_params("")
	{ set(id,name,clockrate,synonym,channels,pTime,maxPTime,bitRate); }

    /**
     * Constructor. Fill this object from an XML element
     * @param xml The element to fill from
     */
    inline JGRtpMedia(XmlElement* xml)
	: m_params("")
	{ fromXml(xml); }

    /**
     * Copy constructor
     */
    inline JGRtpMedia(const JGRtpMedia& src)
	: m_params("") {
	    set(src.m_id,src.m_name,src.m_clockrate,src.m_synonym,src.m_channels,
		src.m_pTime,src.m_maxPTime,src.m_bitRate);
	    m_params = src.m_params;
	}

    /**
     * Set the data
     * @param id The 'id' attribute
     * @param name The 'name' attribute
     * @param clockrate The 'clockrate' attribute
     * @param synonym Application synonym for this payload
     * @param channels Optional 'channels' attribute (the number of channels)
     * @param pTime Optional "ptime" attribute (packet time)
     * @param maxPTime Optional "maxptime" attribute (maximum packet time)
     * @param bitRate Optional "bitrate" attribute
     */
    inline void set(const char* id, const char* name, const char* clockrate,
	const char* synonym = 0, const char* channels = 0,
	const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0) {
	    m_id = id;
	    m_name = name;
	    m_clockrate = clockrate;
	    m_synonym = synonym;
	    m_channels = channels;
	    m_pTime = pTime;
	    m_maxPTime = maxPTime;
	    m_bitRate = bitRate;
	    m_params.clearParams();
	}

    /**
     * Get the string repreasentation (id) of this payload
     * @return The string repreasentation (id) of this payload
     */
    virtual const String& toString() const
	{ return m_id; }

    /**
     * Create a 'payload-type' element from this object
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml() const;

    /**
     * Fill this object from a given element
     * @param xml The element
     */
    void fromXml(XmlElement* xml);

    /**
     * The numeric id of this payload
     */
    String m_id;

    /**
     * The Jingle name of this payload
     */
    String m_name;

    /**
     * The clockrate of this payload
     */
    String m_clockrate;

    /**
     * A synonym of this payload's name
     */
    String m_synonym;

    /**
     * The number of channels
     */
    String m_channels;

    /**
     * Packet time
     */
    String m_pTime;

    /**
     * Maximum packet time
     */
    String m_maxPTime;

    /**
     * Data bit rate
     */
    String m_bitRate;

    /**
     * List of optional parameters
     */
    NamedList m_params;
};

/**
 * This class holds a content description's crypto data.
 * The tag is kept in the String component
 * @short Content crypto data
 */
class YJABBER_API JGCrypto : public String
{
public:
    /**
     * Constructor
     * @param tag The tag parameter
     * @param suite The crypto-suite paramter
     * @param key The key-params parameter
     * @param session The session-param parameter
     */
    inline JGCrypto(const char* tag = "1", const char* suite = 0,
	const char* key = 0, const char* session = 0)
	: String(tag),
	m_suite(suite), m_keyParams(key), m_sessionParams(session)
	{}

    /**
     * Constructor. Build this element from a received element
     * @param xml The received xml element
     */
    inline JGCrypto(const XmlElement* xml)
	{ fromXml(xml); }

    /**
     * Create a 'crypto' element from this object
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml() const;

    /**
     * Build this element from a received element
     * @param xml The received xml element
     */
    void fromXml(const XmlElement* xml);

    /**
     * Build an 'encryption' element from a list of crypto objects
     * @param list The list of crypto objects
     * @param required True if encryption is required
     * @return XmlElement pointer or 0 if the list is empty
     */
    static XmlElement* buildEncryption(const ObjList& list, bool required);

    /**
     * Decode an 'encryption' element. Clear the list before starting
     * @param xml The element to decode
     * @param list The list to be filled with crypto objects
     * @param required Variable to be filled with the value of the 'required' attribute
     */
    static void decodeEncryption(const XmlElement* xml, ObjList& list, bool& required);

    String m_suite;
    String m_keyParams;
    String m_sessionParams;
};

/**
 * Hold a list of RTP data payloads
 * @short A List of Jingle RTP data payloads
 */
class YJABBER_API JGRtpMediaList : public ObjList
{
public:
    /**
     * Media type enumeration
     */
    enum Media {
	MediaMissing = -1,
	MediaUnknown = 0,
	Audio        = 1,
    };

    /**
     * Constructor
     * @param m Media type as enumeration
     * @param cryptoRequired True to require media encryption
     */
    inline JGRtpMediaList(Media m = MediaMissing, bool cryptoRequired = false)
	: m_media(m), m_bandwidth(0), m_cryptoRequired(cryptoRequired), m_ready(false),
	m_telEvent(101), m_telEventName("telephone-event")
	{}

    /**
     * Destructor
     */
    inline ~JGRtpMediaList()
	{ TelEngine::destruct(m_bandwidth); }

    /**
     * Get the media type of the payloads owned by this list
     * @return Media type as enumeration
     */
    inline Media media() const
	{ return m_media; }
 
    /**
     * Append a new data payload
     * @param id The 'id' attribute
     * @param name The 'name' attribute
     * @param clockrate The 'clockrate' attribute
     * @param synonym Optional application synonym for the payload
     * @param channels Optional 'channels' attribute (the number of channels)
     * @param pTime Optional "ptime" attribute (packet time)
     * @param maxPTime Optional "maxptime" attribute (maximum packet time)
     * @param bitRate Optional "bitrate" attribute
     */
    inline void add(const char* id, const char* name, const char* clockrate,
	const char* synonym = 0, const char* channels = 0,
	const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0)
	{ append(new JGRtpMedia(id,name,clockrate,synonym,channels,pTime,maxPTime,bitRate)); }

    /**
     * Reset the list and data
     */
    void reset();

    /**
     * Set media type and payloads from another list
     * @param src Media list to copy into this one
     * @param only Optional list of synonyms to set if found in src.
     *  Copy the whole list if this parameter is empty
     */
    void setMedia(const JGRtpMediaList& src, const String& only = String::empty());

    /**
     * Filter media list preserving only some formats
     * @param only List of synonyms to preserve, do not filter media if this parameter is empty
     */
    void filterMedia(const String& only);

    /**
     * Find a data payload by its id
     * @param id Identifier of media to find
     * @return JGRtpMedia pointer or 0 if not found
     */
    JGRtpMedia* findMedia(const String& id);

    /**
     * Find a data payload by its synonym
     * @param value The value to compare with
     * @return JGRtpMedia pointer or 0 if not found
     */
    JGRtpMedia* findSynonym(const String& value) const;

    /**
     * Create a 'description' element and add payload children to it
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml() const;

    /**
     * Fill this list from an XML element's children. Clear before attempting to fill
     * @param xml The source XML element
     */
    void fromXml(XmlElement* xml);

    /**
     * Create a list from data payloads
     * @param dest Destination string
     * @param synonym True to create from synonyms, false to create from names
     * @param sep List item separator
     * @return False if the list is empty
     */
    bool createList(String& dest, bool synonym, const char* sep = ",");

    /**
     * Build and add telephone-event media child to a parent xml element.
     * Add a second telephone event media child if set
     * @param xml Parent element
     * @param name Optional event name. Defaults to set event name
     */
    void addTelEvent(XmlElement* xml, const char* name = 0) const;

    /**
     * The list of media type names
     */
    static const TokenDict s_media[];

    /**
     * The media type
     */
    Media m_media;

    /**
     * Synchronization source
     */
    String m_ssrc;

    /**
     * Optional SDP media bandwith. The name of the string keeps the type ('bwtype')
     * and its value keeps the actual bandwith
     */
    NamedString* m_bandwidth;

    /**
     * Crypto (SRTP) params
     */
    bool m_cryptoRequired;
    ObjList m_cryptoLocal;
    ObjList m_cryptoRemote;

    /**
     * Flag indicating wether media was negotiated
     */
    bool m_ready;

    /**
     * Telephone event payload id
     */
    int m_telEvent;

    /**
     * Telephone event payload name
     */
    String m_telEventName;

    /**
     * Second telephone event payload name
     */
    String m_telEventName2;
};


/**
 * This class holds a RTP transport candidate
 * @short A RTP transport candidate
 */
class YJABBER_API JGRtpCandidate : public String
{
public:
    /**
     * Constructor
     */
    inline JGRtpCandidate(const char* id, const char* component = "1",
	unsigned int generation = 0, unsigned int net = 0, int prio = 0)
	: String(id),
	m_port(0), m_component(component), m_generation(generation),
	m_network(net), m_priority(prio), m_protocol("udp"), m_type("host")
	{}

    /**
     * Constructor. Build a candidate from received data
     * @param xml Received xml element
     * @param container The transport container
     */
    inline JGRtpCandidate(XmlElement* xml, const JGRtpCandidates& container)
	{ fromXml(xml,container); }

    /**
     * Create a 'candidate' element from this object using local address/port
     * @param container The transport container
     * @return Valid XmlElement pointer if type is a known one
     */
    virtual XmlElement* toXml(const JGRtpCandidates& container) const;

    /**
     * Fill this object from a candidate element using remote address/port
     * @param xml Received xml element
     * @param container The transport container
     */
    void fromXml(XmlElement* xml, const JGRtpCandidates& container);

    String m_address;
    String m_port;
    String m_component;                  // Candidate component
    String m_generation;                 // Candidate generation
    String m_network;                    // NIC card (diagnostic only)
    String m_priority;                   // Candidate priority
    String m_protocol;                   // The only allowable value is "udp"
    String m_type;                       // A Candidate Type as defined in ICE-CORE
};


/**
 * This class holds a RTP transport candidate
 * @short A RTP transport candidate
 */
class YJABBER_API JGRtpCandidateP2P : public JGRtpCandidate
{
    YCLASS(JGRtpCandidateP2P,JGRtpCandidate)
public:
    /**
     * Constructor
     */
    inline JGRtpCandidateP2P()
	: JGRtpCandidate("")
	{}

    /**
     * Constructor. Build a candidate from received data
     * @param xml Received xml element
     * @param container The transport container
     */
    inline JGRtpCandidateP2P(XmlElement* xml, const JGRtpCandidates& container)
	: JGRtpCandidate("")
	{ fromXml(xml,container); }

    /**
     * Create a 'candidate' element from this object using local address/port
     * @param container The transport container
     * @return Valid XmlElement pointer if type is a known one
     */
    virtual XmlElement* toXml(const JGRtpCandidates& container) const;

    /**
     * Fill this object from a candidate element using remote address/port
     * @param xml Received xml element
     * @param container The transport container
     */
    void fromXml(XmlElement* xml, const JGRtpCandidates& container);

    String m_username;
    String m_password;
};


/**
 * This class holds a list of jingle RTP transport candidates
 * @short A list of RTP transport candidates
 */
class YJABBER_API JGRtpCandidates : public ObjList
{
public:
    /**
     * Enumeration of transport types
     */
    enum Type {
	Unknown   = -1,
	RtpIceUdp = 1,
	RtpRawUdp,
	RtpP2P,
	RtpGoogleRawUdp,
    };

    /**
     * Constructor. Fill this object from an XML element
     * @param t The transport type
     */
    inline JGRtpCandidates(Type t = Unknown)
	: m_type(t)
	{}

    /**
     * Get the name of this list's type
     * @return The name of this list's type
     */
    inline const char* typeName() const
	{ return typeName(m_type); }

    /**
     * Fill password and ufrag data
     */
    inline void generateIceAuth() {
	    generateIceToken(m_password,true);
	    generateIceToken(m_ufrag,false);
	}

    /**
     * Fill password and ufrag data using old transport restrictions (16 bytes length)
     */
    inline void generateOldIceAuth() {
	    generateOldIceToken(m_password);
	    generateOldIceToken(m_ufrag);
	}

    /**
     * Find a candidate by its component value
     * @param component The value to search
     * @return JGRtpCandidate pointer or 0
     */
    JGRtpCandidate* findByComponent(unsigned int component);

    /**
     * Create a 'transport' element from this object. Add candidates
     * @param addCandidates True to add the candidate children
     * @param addAuth RtpIceUdp only: add auth data
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml(bool addCandidates, bool addAuth) const;

    /**
     * Fill this object from a given element
     * @param element The element
     */
    void fromXml(XmlElement* element);

    /**
     * Generate a random password or username to be used with ICE-UDP transport
     * @param dest Destination string
     * @param pwd True to generate a password, false to generate an username (ufrag)
     * @param max Maximum number of characters. The maxmimum value is 256.
     *  The minimum value is 22 for password and 4 for username
     */
    static void generateIceToken(String& dest, bool pwd, unsigned int max = 0);

    /**
     * Generate a random password or username to be used with old ICE-UDP transport
     * @param dest Destination string
     */
    static void generateOldIceToken(String& dest);

    /**
     * Get the name associated with a list's type
     * @param t The desired type
     * @param defVal Default value to return
     * @return The name associated with a list's type
     */
    static inline const char* typeName(int t, const char* defVal = "unknown")
	{ return TelEngine::lookup(t,s_type,defVal); }

    /**
     * The list of type names
     */
    static const TokenDict s_type[];

    Type m_type;
    String m_password;
    String m_ufrag;
};


/**
 * This class holds a Jingle content negotiated during a session
 * It can be built from a received xml element and
 *  it can build an xml element from itself
 * @short A Jingle session content
 */
class YJABBER_API JGSessionContent : public RefObject
{
public:
    /**
     * Enumeration of content type
     */
    enum Type {
	Unknown             = -1,        // Unknown
	UnknownFileTransfer = -2,        // Unknown (unsupported) file transfer content
	RtpIceUdp           = 1,         // Audio: RTP ICE-UDP transport
	RtpRawUdp,                       // Audio: RTP RAW-UDP transport
	RtpP2P,                          // 
	RtpGoogleRawUdp,                 //
	FileBSBOffer,                    // File offer: byte stream (SOCKS) transport
	FileBSBRequest,                  // File request: byte stream (SOCKS) transport
    };

    /**
     * Enumeration values for the 'senders' attribute (required)
     */
    enum Senders {
	SendUnknown    = 0,
	SendBoth       = 1,
	SendInitiator  = 2,
	SendResponder  = 3
    };

    /**
     * Enumeration values for the 'creator' attribute (required)
     */
    enum Creator {
	CreatorUnknown   = 0,
	CreatorInitiator = 1,
	CreatorResponder = 2
    };

    /**
     * Constructor
     * @param t Content type as enumeration
     * @param name Content name
     * @param senders Content senders as enumeration
     * @param creator Content creator as enumeration
     * @param disposition Optional content disposition (defauls to 'session' if empty)
     */
    JGSessionContent(Type t, const char* name, Senders senders,
	Creator creator, const char* disposition = 0);

    /**
     * Get the content type
     * @return Content type as enumeration
     */
    inline Type type() const
	{ return m_type; }

    /**
     * Get the senders
     * @return Senders as enumeration
     */
    inline Senders senders() const
	{ return m_senders; }

    /**
     * Get the content creator
     * @return Content creator as enumeration
     */
    inline Creator creator() const
	{ return m_creator; }

    /**
     * Check if this content is a valid audio one: it's media list type is Audio
     *  and the payload list is not empty
     * @return True if this content can be used for audio purposes
     */
    inline bool isValidAudio() const
	{ return (m_rtpMedia.media() == JGRtpMediaList::Audio) && (0 != m_rtpMedia.skipNull()); }

    /**
     * Get the name of this content
     */
    virtual const String& toString() const
	{ return m_name; }

    /**
     * Check if the content disposition is session
     * XEP-0166: true if disposition is missing
     * @return True if this content should be processed at session level
     */
    inline bool isSession() const
	{ return !m_disposition || m_disposition == "session"; }

    /**
     * Check if the content disposition is early media
     * @return True if this content is an early media one
     */
    inline bool isEarlyMedia() const
	{ return m_disposition == "early-session"; }

    /**
     * Set this content's disposition to early media
     */
    inline void setEarlyMedia()
	{ m_disposition = "early-session"; }

    /**
     * Build a 'content' XML element from this object
     * @param minimum Minimum data (only creator and name)
     * @param addDesc True to add the description child
     * @param addTrans True to add the transport child
     * @param addCandidates True to add the transport candidate children
     * @param addAuth RtpIceUdp only: add auth data
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml(bool minimum, bool addDesc,
	bool addTrans, bool addCandidates, bool addAuth) const;

    /**
     * Decode 'content' element attributes
     * @param xml The XML element
     * @param err The error on failure
     * @param error Error text to be sent on failure
     * @return Valid JGSessionContent pointer on success
     */
    static JGSessionContent* fromXml(XmlElement* xml, XMPPError::Type& err,
	String& error);

    /**
     * The list containing the text values for Senders enumeration
     */
    static const TokenDict s_senders[];

    /**
     * The list containing the text values for Creator enumeration
     */
    static const TokenDict s_creator[];

    /**
     * The RTP media description if used
     */
    JGRtpMediaList m_rtpMedia;

    /**
     * The RTP local candidates
     */
    JGRtpCandidates m_rtpLocalCandidates;

    /**
     * The RTP remote candidates
     */
    JGRtpCandidates m_rtpRemoteCandidates;

    /**
     * File info (for file transfer)
     */
    NamedList m_fileTransfer;

private:
    Type m_type;
    String m_name;
    Senders m_senders;
    Creator m_creator;
    String m_disposition;
};


/**
 * This class holds a file transfer stream host definition
 * @short A Jingle file transfer stream host
 */
class JGStreamHost : public String
{
public:
    /**
     * Constructor
     * @param local Local stream host
     * @param jid Stream host jid (id)
     * @param addr Stream host address
     * @param port Stream host port
     * @param zeroConf Optional zero conf definition (override address/port)
     */
    JGStreamHost(bool local, const char* jid, const char* addr, int port, const char* zeroConf = 0)
	: String(jid),
	m_local(local), m_address(addr), m_port(port), m_zeroConf(zeroConf)
	{}

    /**
     * Copy constructor
     * @param src Source stream host to copy from
     */
    inline JGStreamHost(const JGStreamHost& src)
	: String(src),
	m_local(src.m_local), m_address(src.m_address), m_port(src.m_port),
	m_zeroConf(src.m_zeroConf)
	{}

    /**
     * Build an XML element from this stream host
     * @return Valid XmlElement pointer
     */
    XmlElement* toXml();

    /**
     * Build a stream host from an XML element
     * @param xml The element to build from
     * @return Valid JGStreamHost pointer or 0 on error
     */
    static JGStreamHost* fromXml(XmlElement* xml);

    /**
     * Build a query XML element carrying a list of stream hosts
     * @param hosts List of JGStreamHost objects
     * @param sid The query element's sid attribute
     * @param mode The query element's mode attribute
     * @return Valid XmlElement pointer
     */
    static XmlElement* buildHosts(const ObjList& hosts, const char* sid,
	const char* mode = "tcp");

    /**
     * Build a query XML element with a streamhost-used child
     * @param jid The jid of the stream host used
     * @return Valid XmlElement pointer
     */
    static XmlElement* buildRsp(const char* jid);

    bool m_local;
    String m_address;
    int m_port;
    String m_zeroConf;
};


/**
 * This class is a base class for all specific jingle sessions
 * @short A basic Jingle session
 */
class YJABBER_API JGSession : public RefObject, public Mutex
{
    friend class JGEvent;
    friend class JGEngine;
public:
    /**
     * Jingle session version
     */
    enum Version {
	Version0 = 0,
	Version1 = 1,
	VersionUnknown
    };

    /**
     * Jingle defined reasons and errors
     */
    enum Reason {
	ReasonUnknown = 0,
	// Session termination reason
	ReasonOk,                        // success
	ReasonBusy,                      // busy
	ReasonDecline,                   // decline
	ReasonCancel,                    // cancel
	ReasonExpired,                   // expired
	ReasonConn,                      // connectivity-error
	ReasonFailApp,                   // failed-application
	ReasonFailTransport,             // failed-transport
	ReasonGone,                      // gone
	ReasonParams,                    // incompatible-parameters
	ReasonMedia,                     // media-error
	ReasonTransport,                 // unsupported-transports
	ReasonApp,                       // unsupported-applications
	ReasonSecurity,                  // security-error
	ReasonTimeout,                   // timeout
	ReasonGeneral,                   // general-error
	ReasonAltSess,                   // alternative-session
	// Session transfer (XEP 0251)
	Transferred,                     // transferred
	// RTP session errors (XEP 0167)
	CryptoRequired,                  // crypto-required
	InvalidCrypto,                   // invalid-crypto
    };

    /**
     * RTP session info (XEP 0167)
     */
    enum RtpInfo {
	RtpActive,                       // active
	RtpHold,                         // hold
	RtpMute,                         // mute
	RtpRinging,                      // ringing
    };

    /**
     * Session state enumeration
     */
    enum State {
	Idle    = 0,                     // Outgoing stream is waiting for 
	Pending = 1,                     // Session is pending, session-initiate sent/received
	Active  = 2,                     // Session is active, session-accept sent/received
	Ending  = 3,                     // Session terminated: Wait for write result
	Destroy = 4,                     // The session will be destroyed
    };

    /**
     * Jingle action enumeration
     */
    enum Action {
	ActAccept,                       // session-accept
	ActInitiate,                     // session-initiate
	ActTerminate,                    // session-terminate
	ActReject,                       // reject
	ActInfo,                         // session-info
	ActTransportInfo,                // transport-info
	ActTransportAccept,              // transport-accept
	ActTransportReject,              // transport-reject
	ActTransportReplace,             // transport-replace
	ActCandidates,                   // candidates
	ActContentAccept,                // content-accept
	ActContentAdd,                   // content-add
	ActContentModify,                // content-modify
	ActContentReject,                // content-reject
	ActContentRemove,                // content-remove
	ActContentInfo,                  // content-info
	ActDescriptionInfo,              // description-info
	ActTransfer,                     // session-info: Transfer
	ActRinging,                      // session-info: Ringing
	ActTrying,                       // session-info: Trying
	ActReceived,                     // session-info: Received
	ActHold,                         // session-info: Hold
	ActActive,                       // session-info: Active
	ActMute,                         // session-info: Mute
	ActDtmf,                         // session-info: Dtmf
	ActStreamHost,
	ActCount,
    };

    /**
     * Session flags
     */
    enum SessionFlag {
	FlagNoPing = 0x0001,             // Don't send ping
	FlagRingNsRtp = 0x0002,          // Send ringing using rtp namespace instead of rtp info namespace
	FlagNoOkInitiate = 0x0004,       // Don't raise a ResultOk when initiate stanza is confirmed
    };

    /**
     * Destructor
     */
    virtual ~JGSession();

    /**
     * Get the session version
     * @return The session version
     */
    inline Version version() const
	{ return m_version; }

    /**
     * Retrieve the engine owning this session
     * @return The engine owning this session
     */
    inline JGEngine* engine() const
	{ return m_engine; }

    /**
     * Get the session direction
     * @return True if it is an outgoing session
     */
    inline bool outgoing() const
	{ return m_outgoing; }

    /**
     * Get the session id
     * @return The session id
     */
    inline const String& sid() const
	{ return m_sid; }

    /**
     * Get the local peer's JID
     * @return The local peer's JID
     */
    inline const JabberID& local() const
	{ return m_local; }

    /**
     * Get the remote peer's JID
     * @return The remote peer's JID
     */
    inline const JabberID& remote() const
	{ return m_remote; }

    /**
     * Get the session state.
     * @return The session state as enumeration.
     */
    inline State state() const
	{ return m_state; }

    /**
     * Retrieve session flags
     * @param mask Mask to retrieve
     * @return Session flags
     */
    inline int flag(int mask) const
	{ return m_flags & mask; }

    /**
     * Replace session flags
     * @param value The new session flags
     */
    inline void setFlags(int value)
	{ m_flags = value; }

    /**
     * Get the arbitrary user data of this session
     * @return The arbitrary user data of this session
     */
    inline void* userData()
	{ return m_private; }

    /**
     * Set the arbitrary user data of this session
     * @param userdata The new arbitrary user data's value
     */
    inline void userData(void* userdata)
	{ m_private = userdata; }

    /**
     * Retrieve the client account used by this session
     * @return The client account used by this session
     */
    inline const String& line() const
	{ return m_line; }

    /**
     * Set the client account used by this session
     * @param acc The client account used by this session
     */
    inline void line(const String& acc)
	{ m_line = acc; }

    /**
     * Get an action (jingle element type) from a jingle element
     * @param xml Element to check
     * @return The found action, ActCount if not found or unknown
     */
    Action getAction(XmlElement* xml);

    /**
     * Ask this session to accept an incoming xml 'iq' element
     * @param type Iq type as enumeration
     * @param from The sender
     * @param to The recipient
     * @param id The session id of this is a request (set/get) or the stanza id
     * @param xml The received element
     * @return True if accepted (the element was enqueued), false if not
     */
    bool acceptIq(XMPPUtils::IqType type, const JabberID& from, const JabberID& to,
	const String& id, XmlElement* xml);

    /**
     * Confirm (send result) a received element
     * @param xml The element to confirm
     * @return False if send failed or element is 0
     */
    bool confirmResult(XmlElement* xml);

    /**
     * Confirm (send error) a received element
     * @param xml The element to confirm (will be consumed and zeroed)
     * @param error The error condition
     * @param text Optional text to add to the error element
     * @param type Error type
     * @return False if send failed or element is 0
     */
    bool confirmError(XmlElement*& xml, XMPPError::Type error,
	const char* text = 0, XMPPError::ErrorType type = XMPPError::TypeModify);

    /**
     * Accept a Pending incoming session.
     * This method is thread safe
     * @param contents The list of accepted contents
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool accept(const ObjList& contents, String* stanzaId = 0)
	{ return false; }

    /**
     * Close a Pending or Active session
     * This method is thread safe
     * @param reason Optional termination reason
     * @return False if send failed
     */
    virtual bool hangup(XmlElement* reason = 0);

    /**
     * Create a RTP info child to be added to a session-info element
     * @param info The informational tag as enumeration
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createRtpInfoXml(RtpInfo info)
	{ return 0; }

    /**
     * Create a termination reason element
     * @param reason The reason code
     * @param text Optional reason text child
     * @param child Optional additional reason child
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createReason(int reason, const char* text = 0,
	XmlElement* child = 0)
	{ return 0; }

    /**
     * Create a transfer reason element
     * @param reason The reason code
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createTransferReason(int reason)
	{ return 0; }

    /**
     * Create a RTP session reason element
     * @param reason The reason code
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createRtpSessionReason(int reason)
	{ return 0; }

    /**
     * Send a stanza with session content(s)
     * This method is thread safe
     * @param action Must be a transport- or content- action
     * @param contents Non empty list with content(s) to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0)
	{ return false; }

    /**
     * Send a stanza with a session content
     * This method is thread safe
     * @param action Must be a transport- or content- action
     * @param content The content to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    inline bool sendContent(Action action, const JGSessionContent* content,
	String* stanzaId = 0) {
	    if (!content)
		return false;
	    ObjList tmp;
	    tmp.append(content)->setDelete(false);
	    return sendContent(action,tmp,stanzaId);
	}

    /**
     * Send a stanza with stream hosts
     * This method is thread safe
     * @param hosts The list of hosts to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool sendStreamHosts(const ObjList& hosts, String* stanzaId = 0)
	{ return false; }

    /**
     * Send a stanza with a stream host used. If the jid is empty, send an
     *  item-not-found error response
     * This method is thread safe
     * @param jid The stream host to send
     * @param stanzaId The id of the stanza to confirm
     * @return False if send failed
     */
    virtual bool sendStreamHostUsed(const char* jid, const char* stanzaId)
	{ return false; }

    /**
     * Build SOCKS SHA1 dst.addr used by file transfer
     * @param buf Destination string
     */
    void buildSocksDstAddr(String& buf);

    /**
     * Send a session info element to the remote peer.
     * This method is thread safe
     * @param xml The XmlElement carried by the session info element
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @param extra Optional extra child for jingle element
     * @return False on failure
     */
    bool sendInfo(XmlElement* xml, String* stanzaId = 0, XmlElement* extra = 0);

    /**
     * Send a dtmf string to remote peer. If the string's length is greater then 1, each
     *  character is added as a 'dtmf' child of the jingle element
     * @param dtmf The dtmf string
     * @param msDuration The tone duration in miliseconds. Ignored if 0
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @return False if send failed
     */
    bool sendDtmf(const char* dtmf, unsigned int msDuration = 0, String* stanzaId = 0);

    /**
     * Check if the remote party supports a given feature
     * @param feature The requested feature
     * @return True if the remote party supports the given feature
     */
    bool hasFeature(XMPPNamespace::Type feature);

    /**
     * Build a transfer element
     * @param transferTo The JID to transfer to
     * @param transferFrom The transferror's JID
     * @param sid Optional session id used for attended transfer (empty for unattended transfer)
     * @return Valid XmlElement pointer
     */
    static XmlElement* buildTransfer(const String& transferTo, const String& transferFrom,
	const String& sid = String::empty());

     /**
     * Get the session version associated with a text
     * @param value The version text
     * @param def Default value to return if not found
     * @return Session Version value
     */
    static inline Version lookupVersion(const char* value, Version def = VersionUnknown)
	{ return (Version)lookup(value,s_versions,def); }

     /**
     * Get the session version name
     * @param value The version value
     * @param def Default value to return if not found
     * @return Session version name or the default value if not found
     */
    static inline const char* lookupVersion(int value, const char* def = "unknown")
	{ return lookup(value,s_versions,def); }

     /**
     * Get the termination code associated with a text
     * @param value The termination text
     * @param def Default value to return if not found
     * @return Termination code
     */
    static inline int lookupReason(const char* value, int def = ReasonOk)
	{ return lookup(value,s_reasons,def); }

    /**
     * Get the termination code associated with a text
     * @param value The termination code
     * @param def Default value to return if not found
     * @return Termination text
     */
    static inline const char* lookupReason(int value, const char* def = 0)
	{ return lookup(value,s_reasons,def); }

    /**
     * Get the name of a session state
     * @param state The state to find
     * @return The name of a session state
     */
    static const char* lookupState(int state)
	{ return lookup(state,s_states); }

    /**
     * Get the name of an action
     * @param act The action to find
     * @param ver Session version to use
     * @return The name of an action
     */
    static const char* lookupAction(int act, Version ver);

    /**
     * Get the action associated with a given string
     * @param str The action name
     * @param ver Session version to use
     * @return The name of an action
     */
    static Action lookupAction(const char* str, Version ver);

    /**
     * Session version names
     */
    static const TokenDict s_versions[];

    /**
     * Termination reasons and errors
     */
    static const TokenDict s_reasons[];

    /**
     * RTP session info (XEP 0167)
     */
    static const TokenDict s_rtpInfo[];

    /**
     * Session state names
     */
    static const TokenDict s_states[];

    /**
     * Action names for version Version0
     */
    static const TokenDict s_actions0[];

    /**
     * Action names for version Version1
     */
    static const TokenDict s_actions1[];

    /**
     * Session flag names
     */
    static const TokenDict s_flagName[];

protected:
    /**
     * Constructor. Create an outgoing session
     * @param ver The session version
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     */
    JGSession(Version ver, JGEngine* engine,
	const JabberID& caller, const JabberID& called);

    /**
     * Constructor. Create an incoming session.
     * @param ver The session version
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     * @param xml A valid Jabber Jingle xml with action session initiate
     * @param id Session id
     */
    JGSession(Version ver, JGEngine* engine, const JabberID& caller,
	const JabberID& called, XmlElement* xml, const String& id);

    /**
     * Build and send the initial message on an outgoing session
     * @param contents The session contents to be sent with session initiate element
     * @param extra Optional extra child to be added to the session initiate element
     * @param subject Optional session subject
     * @return True on success
     */
    virtual bool initiate(const ObjList& contents, XmlElement* extra,
	const char* subject = 0) = 0;

    /**
     * Get a Jingle event from the queue.
     * This method is thread safe
     * @param time Current time in miliseconds
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* getEvent(u_int64_t time);

    /**
     * Release this session and its memory
     */
    virtual void destroyed();

    /**
     * Send a stanza to the remote peer
     * @param stanza The stanza to send
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @param confirmation True if the stanza needs confirmation (add 'id' attribute)
     * @param ping True if the stanza is a ping one
     * @param toutMs Optional stanza timeout interval in milliseconds
     * @return True on success
     */
    bool sendStanza(XmlElement* stanza, String* stanzaId = 0, bool confirmation = true,
	bool ping = false, unsigned int toutMs = 0);

    /**
     * Send a ping (empty session info) stanza to the remote peer if it's time to do it
     * @param msecNow The current time
     * @return True if a ping was succesfully sent
     */
    bool sendPing(u_int64_t msecNow);

    /**
     * Decode a jingle element
     * @param xml The element to decode
     * @param child The element's first child
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child) = 0;

    /**
     * Create an 'iq' of type 'set' with a 'jingle' child
     * @param action The action of the Jingle stanza
     * @param element1 Optional child element
     * @param element2 Optional child element
     * @param element3 Optional child element
     * @return Valid XmlElement pointer
     */
    virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0,
	XmlElement* element2 = 0, XmlElement* element3 = 0) = 0;

    /**
     * Create a dtmf XML element
     * @param dtmf The dtmf string
     * @param msDuration The tone duration in miliseconds. Ignored if 0
     * @return Valid XmlElement pointer or 0
     */
    virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0) = 0;

    /**
     * Method called in getEvent() to process a last event decoded from a
     *  received jingle element
     * @param ev The event to process (will be consumed and zeroed)
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* processJingleSetEvent(JGEvent*& ev);

    /**
     * Method called in getEvent() to process a jabber event carrying a response
     * @param result True if the element is a result, false if it's an error response
     * @param xml Xml element to process
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* processJabberIqResponse(bool result, XmlElement*& xml);

    /**
     * Decode a file transfer element
     * @param set True if the xml is an iq 'set', false if type is 'get'
     * @param xml The element to decode
     * @param child The element's first child
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* processFileTransfer(bool set, XmlElement*& xml, XmlElement* child);

    /**
     * Terminate notification from an event. Reset the last generated event
     * @param event Terminated (processed) event
     */
    void eventTerminated(JGEvent* event);

    /**
     * Changed session state
     * @param newState Session new state
     */
    void changeState(State newState);

    Version m_version;                   // Session version
    State m_state;                       // Session state
    int m_flags;                         // Session flags
    u_int64_t m_timeToPing;              // Time to send ping (empty session-info)
    JGEngine* m_engine;                  // The engine that owns this session
    bool m_outgoing;                     // Session direction
    String m_sid;                        // Session id
    JabberID m_local;                    // Local peer's JID
    JabberID m_remote;                   // Remote peer's JID
    XmlFragment m_queue;                 // Incoming, unprocessed, xml elements
    JGEvent* m_lastEvent;                // Last generated event
    bool m_recvTerminate;                // Flag indicating whether session-terminate was received
    void* m_private;                     // Arbitrary user data
    String m_localSid;                   // Local session id (used to generate element's id)
    u_int32_t m_stanzaId;                // Sent stanza id counter
    ObjList m_sentStanza;                // Sent stanzas' id
    String m_line;                       // Session account

private:
    JGSession() {}                       // Don't use it
};


/**
 * A session implementing the old jingle protocol
 * @short The version 0 of a jingle session
 */
class YJABBER_API JGSession0 : public JGSession
{
    friend class JGEvent;
    friend class JGEngine;
public:
    /**
     * Destructor
     */
    virtual ~JGSession0();

    /**
     * Accept a Pending incoming session.
     * This method is thread safe
     * @param contents The list of accepted contents
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool accept(const ObjList& contents, String* stanzaId = 0);

protected:
    /**
     * Constructor. Create an outgoing session
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     */
    JGSession0(JGEngine* engine, const JabberID& caller, const JabberID& called);

    /**
     * Constructor. Create an incoming session.
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     * @param xml A valid Jabber Jingle xml with action session initiate
     * @param id Session id
     */
    JGSession0(JGEngine* engine, const JabberID& caller, const JabberID& called,
	XmlElement* xml, const String& id);

    /**
     * Build and send the initial message on an outgoing session
     * @param contents The session contents to be sent with session initiate element
     * @param extra Optional extra child to be added to the session initiate element
     * @param subject Optional session subject
     * @return True on success
     */
    virtual bool initiate(const ObjList& contents, XmlElement* extra,
	const char* subject = 0);

    /**
     * Send a stanza with session content(s)
     * This method is thread safe
     * @param action Must be a transport- action
     * @param contents Non empty list with content(s) to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0);

    /**
     * Decode a jingle element
     * @param xml The element to decode
     * @param child The element's first child
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child);

    /**
     * Create an 'iq' of type 'set' with a 'jingle' child
     * @param action The action of the Jingle stanza
     * @param element1 Optional child element
     * @param element2 Optional child element
     * @param element3 Optional child element
     * @return Valid XmlElement pointer
     */
    virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0,
	XmlElement* element2 = 0, XmlElement* element3 = 0);

    /**
     * Create a dtmf XML element
     * @param dtmf The dtmf string
     * @param msDuration The tone duration in miliseconds. Ignored if 0
     * @return Valid XmlElement pointer or 0
     */
    virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0);

protected:
    String m_sessContentName;            // Content name advertised to upper layer
    Action m_candidatesAction;           // Use candidates/transport-info for candidates
};

/**
 * A session implementing the Jingle protocol including session transfer and file transfer
 * @short The version 1 of a jingle session
 */
class YJABBER_API JGSession1 : public JGSession
{
    friend class JGEvent;
    friend class JGEngine;
public:
    /**
     * Destructor
     */
    virtual ~JGSession1();

    /**
     * Accept a Pending incoming session.
     * This method is thread safe
     * @param contents The list of accepted contents
     * @param stanzaId Optional string to be filled with sent stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool accept(const ObjList& contents, String* stanzaId = 0);

    /**
     * Create a RTP info child to be added to a session-info element
     * @param info The informational tag as enumeration
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createRtpInfoXml(RtpInfo info);

    /**
     * Create a termination reason element
     * @param reason The reason code
     * @param text Optional reason text child
     * @param child Optional additional reason child
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createReason(int reason, const char* text = 0,
	XmlElement* child = 0);

    /**
     * Create a transfer reason element
     * @param reason The reason code
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createTransferReason(int reason);

    /**
     * Create a RTP session reason element
     * @param reason The reason code
     * @return Valid XmlElement pointer or 0 if not supported
     */
    virtual XmlElement* createRtpSessionReason(int reason);

    /**
     * Send a stanza with session content(s)
     * This method is thread safe
     * @param action Must be a transport- or content- action
     * @param contents Non empty list with content(s) to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0);

    /**
     * Send a stanza with stream hosts
     * This method is thread safe
     * @param hosts The list of hosts to send
     * @param stanzaId Optional string to be filled with sent
     *  stanza id (used to track the response)
     * @return False if send failed
     */
    virtual bool sendStreamHosts(const ObjList& hosts, String* stanzaId = 0);

    /**
     * Send a stanza with a stream host used. If the jid is empty, send an
     *  item-not-found error response
     * This method is thread safe
     * @param jid The stream host to send
     * @param stanzaId The id of the stanza to confirm
     * @return False if send failed
     */
    virtual bool sendStreamHostUsed(const char* jid, const char* stanzaId);

protected:
    /**
     * Constructor. Create an outgoing session
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     */
    JGSession1(JGEngine* engine, const JabberID& caller, const JabberID& called);

    /**
     * Constructor. Create an incoming session.
     * @param engine The engine owning this session
     * @param caller The caller's full JID
     * @param called The called party's full JID
     * @param xml A valid Jabber Jingle xml with action session initiate
     * @param id Session id
     */
    JGSession1(JGEngine* engine, const JabberID& caller, const JabberID& called,
	XmlElement* xml, const String& id);

    /**
     * Build and send the initial message on an outgoing session
     * @param contents The session contents to be sent with session initiate element
     * @param extra Optional extra child to be added to the session initiate element
     * @param subject Optional session subject
     * @return True on success
     */
    virtual bool initiate(const ObjList& contents, XmlElement* extra,
	const char* subject = 0);

    /**
     * Decode a jingle element
     * @param xml The element to decode
     * @param child The element's first child
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child);

    /**
     * Create an 'iq' of type 'set' with a 'jingle' child
     * @param action The action of the Jingle stanza
     * @param element1 Optional child element
     * @param element2 Optional child element
     * @param element3 Optional child element
     * @return Valid XmlElement pointer
     */
    virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0,
	XmlElement* element2 = 0, XmlElement* element3 = 0);

    /**
     * Create a dtmf XML element
     * @param dtmf The dtmf string
     * @param msDuration The tone duration in miliseconds. Ignored if 0
     * @return Valid XmlElement pointer or 0
     */
    virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0);

    /**
     * Decode a file transfer element
     * @param set True if the xml is an iq 'set', false if type is 'get'
     * @param xml The element to decode
     * @param child The element's first child
     * @return JGEvent pointer or 0
     */
    virtual JGEvent* processFileTransfer(bool set, XmlElement*& xml, XmlElement* child);

};

/**
 * This class holds an event generated by a Jingle session
 * @short A Jingle event
 */
class YJABBER_API JGEvent
{
    friend class JGSession;
    friend class JGSession0;
    friend class JGSession1;
public:
    /**
     * Jingle events enumeration
     */
    enum Type {
	Jingle,                          //
	ResultOk,                        // Response for a sent stanza (iq with type=result)
	ResultError,                     // Response for a sent stanza (iq with type=error)
	ResultTimeout,                   // Response for a sent stanza (stanza timeout)
	// Final
	Terminated,                      // m_element is the element that caused the termination
	                                 //  m_reason contains the reason
	Destroy,                         // The engine sould delete the event (causing session destruction)
    };

    /**
     * Destructor. Deref the session. Delete the XML element
     */
    virtual ~JGEvent();

    /**
     * Get the type of this event
     * @return The type of this event as enumeration
     */
    inline Type type() const
	{ return m_type; }

    /**
     * Get the name of this
     * @return The name of this event
     */
    inline const char* name()
	{ return lookupType(m_type); }

    /**
     * Get the session that generated this event
     * @return The session that generated this event
     */
    inline JGSession* session() const
	{ return m_session; }

    /**
     * Get the XML element that generated this event
     * @return The XML element that generated this event
     */
    inline XmlElement* element() const
	{ return m_element; }

    /**
     * Get the Jingle child of the XML element carried by the event
     * Don't delete it after use: it is owned by the event
     * @return The Jingle child of the XML element carried by the event
     */
    inline XmlElement* jingle() const
	{ return m_jingle; }

    /**
     * Get the jingle action as enumeration
     * @return The jingle action as enumeration
     */
    inline JGSession::Action action() const
	{ return m_action; }

    /**
     * Get the name of an action
     * @return The name of an action
     */
    inline const char* actionName() const
	{ return m_session ? JGSession::lookupAction(m_action,m_session->version()) : ""; }

    /**
     * Get the id
     * @return The id
     */
    inline const String& id() const
	{ return m_id; }

    /**
     * Get the reason
     * @return The reason
     */
    inline const String& reason() const
	{ return m_reason; }

    /**
     * Get the text
     * @return The text
     */
    inline const String& text() const
	{ return m_text; }

    /**
     * Get the XML element that generated this event and set it to 0
     * @return The XML element that generated this event
     */
    inline XmlElement* releaseXml() {
	    XmlElement* tmp = m_element;
	    m_jingle = m_element = 0;
	    return tmp;
	 }

    /**
     * Check if this event is a final one (Terminated or Destroy)
     * @return True if it is
     */
    inline bool final() const
	{ return m_type == Terminated || m_type == Destroy; }

    /**
     * Confirm the element carryied by this event. See JGSession::confirm() for details
     * @param error The error condition
     * @param text Optional text to add to the error element
     * @param type Error type
     * @return False if send failed or element is 0
     */
    inline bool confirmElement(XMPPError::Type error = XMPPError::NoError,
	const char* text = 0, XMPPError::ErrorType type = XMPPError::TypeModify) {
	    if (m_session && element() && !m_confirmed) {
		m_confirmed = true;
		if (error == XMPPError::NoError)
		    return m_session->confirmResult(element());
		XmlElement* err = releaseXml();
		return m_session->confirmError(err,error,text,type);
	    }
	    return false;
	}

    /**
     * Set the confirmed flag. Use it for action with delayed response
     */
    inline void setConfirmed()
	{ m_confirmed = true; }

    /**
     * Set the jingle action as enumeration. Set confirmation flag if
     *  the element don't require it
     * @param act The jingle action as enumeration
     */
    void setAction(JGSession::Action act);

    /**
     * Get the name of an event type
     * @return The name of an event type
     */
    static inline const char* lookupType(int type)
	{ return lookup(type,s_typeName); }

    /**
     * Dictionary with event type names
     */
    static const TokenDict s_typeName[];

    /**
     * The list of session contents if used
     */
    ObjList m_contents;

    /**
     * The list of stream hosts if used
     */
    ObjList m_streamHosts;

protected:
    /**
     * Constructor. Set the id parameter if the element is valid
     * @param type Event type
     * @param session The session that generated this event
     * @param element Optional XML element that generated this event
     * @param reason Optional reason data
     * @param text Optional text data
     */
    inline JGEvent(Type type, JGSession* session, XmlElement* element = 0,
	const char* reason = 0, const char* text = 0)
	: m_type(type), m_confirmed(true), m_session(0), m_element(element),
	m_jingle(0), m_action(JGSession::ActCount), m_reason(reason), m_text(text)
	{ init(session); }

    /**
     * Constructor. Create a Jingle event. Set the id parameter if the element is valid
     * @param act The jingle action
     * @param session The session that generated this event
     * @param element XML element that generated this event
     * @param reason Optional reason data
     * @param text Optional text data
     */
    inline JGEvent(JGSession::Action act, JGSession* session, XmlElement* element,
	const char* reason = 0, const char* text = 0)
	: m_type(Jingle), m_confirmed(false), m_session(0), m_element(element), m_jingle(0),
	m_action(act), m_reason(reason), m_text(text) {
	    init(session);
	    setAction(act);
	}

private:
    JGEvent() {}                         // Don't use it
    void init(JGSession* session);

    Type m_type;                         // The type of this event
    bool m_confirmed;                    // Flag indicating that element was confirmed
    JGSession* m_session;                // Jingle session that generated this event
    XmlElement* m_element;               // XML element that generated this event
    XmlElement* m_jingle;                // The session child, if present
    // Event specific
    JGSession::Action m_action;          // The action if type is Jingle
    String m_id;                         // The element's id attribute
    String m_reason;                     // The reason if type is Error or Terminated 
    String m_text;                       // Dtmf text
};

/**
 * This class holds a Jingle service for the Jabber engine. Handle jingle stanzas,
 *  stanza write fail events and stream termination events
 * @short A Jingle engine
 */
class YJABBER_API JGEngine : public DebugEnabler, public Mutex
{
    friend class JGSession;
public:
    /**
     * Constructor
     * @param name Debug name
     */
    JGEngine(const char* name = "jgengine");

    /**
     * Destructor. Terminates all active sessions
     */
    virtual ~JGEngine();

    /**
     * Retrieve the default session flags value
     * @return The default session flags value
     */
    inline int sessionFlags() const
	{ return m_sessionFlags; }

    /**
     * Get the timeout interval of a sent stanza
     * @return The timeout interval of a sent stanza
     */
    inline u_int64_t stanzaTimeout() const
	{ return m_stanzaTimeout; }

    /**
     * Get the timeout interval of a sent stream host stanza
     * @return The timeout interval of a sent stream host stanza
     */
    inline u_int64_t streamHostTimeout() const
	{ return m_streamHostTimeout; }

    /**
     * Get the ping interval used by jingle sessions
     * @return The interval to ping the remote party of a jingle session
     */
    inline u_int64_t pingInterval() const
	{ return m_pingInterval; }

    /**
     * Initialize this service
     * @param params Service's parameters
     */
    virtual void initialize(const NamedList& params);

    /**
     * Send a session's stanza.
     * This method should be re-implemented 
     * @param session The session requesting the operation
     * @param stanza The stanza to send. Will be consumed and zeroed
     * @return True on success
     */
    virtual bool sendStanza(JGSession* session, XmlElement*& stanza);

    /**
     * Send a chat message on behalf of a session
     * @param session The session requesting the operation
     * @param body Message body
     * @return True on success
     */
    virtual bool sendMessage(JGSession* session, const char* body);

    /**
     * Call getEvent() for each session list until an event is generated or the end is reached
     * This method is thread safe
     * @param time Current time in miliseconds
     * @return The first generated event
     */
    JGEvent* getEvent(u_int64_t time);

    /**
     * Make an outgoing call.
     * 'media' and 'transport' will be invalid on exit. Don't delete them
     * @param ver The session version to use
     * @param caller The caller
     * @param called The called
     * @param contents The list of session content(s)
     * @param extra Optional extra child for session initiate element
     * @param msg Optional message to send before call
     * @param subject Optional session subject
     * @param line Optional session account
     * @param flags Optional session flags to set
     * @return Valid JGSession pointer (referenced) on success
     */
    JGSession* call(JGSession::Version ver, const JabberID& caller, const JabberID& called,
	const ObjList& contents, XmlElement* extra = 0, const char* msg = 0,
	const char* subject = 0, const char* line = 0, int* flags = 0);

    /**
     * Ask this engine to accept an incoming xml 'iq' element
     * @param type Iq type as enumeration
     * @param from The sender
     * @param to The recipient
     * @param id Element id attribute
     * @param xml The received element
     * @param line Account receiving the stanza (may be empty)
     * @param error XMPPError result. This value should be check if false is returned.
     *  Any value different from NoError indicate an invalid element
     * @param text Error text
     * @return True if accepted (don't use the given pointer if accepted)
     */
    bool acceptIq(XMPPUtils::IqType type, const JabberID& from, const JabberID& to,
	const String& id, XmlElement* xml, const char* line,
	XMPPError::Type& error, String& text);

    /**
     * Default event processor. Delete event.
     * @param event The event to process
     */
    void defProcessEvent(JGEvent* event);

    /**
     * Process events from the sessions. Default action: Delete event.
     * Descendants must override this method to process generated events
     * @param event The event to process
     */
    virtual void processEvent(JGEvent* event);

    /**
     * Decode a comma separated list of flags
     * @param list The list of flags
     * @param dict Dictionary to use
     * @return Found flags
     */
    static int decodeFlags(const String& list, const TokenDict* dict);

    /**
     * Encode (append) flags to a comma separated list
     * @param buf Destination buffer
     * @param flags Flags to encode
     * @param dict Dictionary to use
     */
    static void encodeFlags(String& buf, int flags, const TokenDict* dict);

private:
    // Create a local session id
    void createSessionId(String& id);

    ObjList m_sessions;                  // List of sessions
    u_int32_t m_sessionId;               // Session id counter
    u_int64_t m_stanzaTimeout;           // The timeout of a sent stanza
    u_int64_t m_streamHostTimeout;       // The timeout of a sent stream host stanza
    u_int64_t m_pingInterval;            // Interval to send ping (empty session-info)
    int m_sessionFlags;                  // Default session flags
};


/**
 * This class holds sent stanzas info used for timeout checking
 * @short Send stanza timeout info
 */
class YJABBER_API JGSentStanza : public String
{
public:
    /**
     * Constructor
     * @param id The sent stanza's id
     * @param time The sent time
     * @param notif True to notify stanza timeout or response
     * @param ping True if the sent stanza is a ping one
     * @param action Optional sent stanza action
     */
    JGSentStanza(const char* id, u_int64_t time, bool notif = false, bool ping = false,
	JGSession::Action action = JGSession::ActCount)
	: String(id), m_time(time), m_notify(notif), m_ping(ping), m_action(action)
	{}

    /**
     * Retrieve stanza timeout
     * @return Stanza timeout
     */
    inline u_int64_t timeout() const
	{ return m_time; }

    /**
     * Check if this element timed out
     * @return True if timeout
     */
    inline bool timeout(u_int64_t time) const
	{ return time > m_time; }

    /**
     * Check if timeout should be notified to sender
     * @return True to notify
     */
    inline bool notify() const
	{ return m_notify; }

    /**
     * Check if the stanza is a ping one
     * @return True if the stanza is a ping one
     */
    inline bool ping() const
	{ return m_ping; }

    /**
     * Get the jingle action as enumeration
     * @return The jingle action as enumeration
     */
    inline JGSession::Action action() const
	{ return m_action; }

private:
    u_int64_t m_time;                    // Timeout
    bool m_notify;                       // Notify timeout to sender
    bool m_ping;                         // Sent stanza is a ping one
    JGSession::Action m_action;          // Sent stanza action
};

};

#endif /* __YATEJINGLE_H */

/* vi: set ts=8 sw=4 sts=4 noet: */
